#include "napi/native_api.h"

/**
 * 定义一个加法运算函数
 * 函数声明：符合 N-API 标准的函数签名
 * 
 * @param env  N-API 环境句柄，用于管理 JavaScript 运行时环境
 * @param info  包含函数调用信息的结构体（参数、this 值等）
 * @return
 */
static napi_value Add(napi_env env, napi_callback_info info)
{
    // 定义期望接收的参数数量
    size_t argc = 2;
    // 创建参数数组，长度为2，初始化为 nullptr
    napi_value args[2] = {nullptr};

    // 从回调信息中提取参数，将实际传入的参数填充到 args 数组中
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    // 检查第一个参数的类型
    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    // 检查第二个参数的类型
    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    // 将第一个 JavaScript 值转换为 C 的 double 类型，并存放到value0变量中
    double value0;
    napi_get_value_double(env, args[0], &value0);

    // 将第二个 JavaScript 值转换为 C 的 double 类型，并存放到value1变量中
    double value1;
    napi_get_value_double(env, args[1], &value1);

    // 创建返回值的 napi_value
    napi_value sum;
    // 将 C 的 double 类型结果转换回 JavaScript 的 number 类型
    napi_create_double(env, value0 + value1, &sum);

    // 返回计算结果给 JavaScript/ArkTS 层
    return sum;
}

/**
 * 定义一个乘法运算函数，完整的改进版本（带错误处理）
 * 函数声明：符合 N-API 标准的函数签名
 * 
 * @param env  N-API 环境句柄，用于管理 JavaScript 运行时环境
 * @param info  包含函数调用信息的结构体（参数、this 值等）
 * @return
 */
static napi_value Multiply(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    // 获取调用信息
    napi_status status = napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "解析参数失败！");
        return nullptr;
    }

    // 验证参数数量
    if (argc < 2) {
        napi_throw_error(env, nullptr, "参数数量错误");
        return nullptr;
    }
    
    // 验证参数类型
    napi_valuetype valuetype0, valuetype1;
    napi_typeof(env, args[0], &valuetype0);
    napi_typeof(env, args[1], &valuetype1);
    if (valuetype0 != napi_number || valuetype1 != napi_number) {
        napi_throw_error(env, nullptr, "预期参数类型错误");
        return nullptr;
    }

    // 提取数值
    double value0, value1;
    napi_get_value_double(env, args[0], &value0);
    napi_get_value_double(env, args[1], &value1);

    // 计算并返回结果
    napi_value sum;
    napi_create_double(env, value0 * value1, &sum);

    return sum;
}

/**
 * EXTERN_C_START 和 EXTERN_C_END：
    这两个宏用于确保中间的代码以C语言的方式编译和链接，防止C++的名称修饰（name mangling）。这样，在动态链接时能够正确找到函数。
 * 这是 C++ 的兼容性宏，确保代码在 C 和 C++ 环境下都能正确编译
 * 防止 C++ 的 name mangling（名称修饰）
 * 
 * Init 函数：
 *      这是一个模块初始化函数，在模块被加载时调用
 * Init函数作用：批量定义属性到 exports 对象，用于初始化插件，用于将ArkTS层的函数或属性与C++层的函数进行关联
 * 
 * @param env       N-API环境句柄
 * @param exports   表示模块导出对象的napi_value，相当于ArkTS中的exports对象
 * @return
 */
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    // 定义了一个napi_property_descriptor数组desc，每个元素描述一个导出属性（这里导出两个函数：add和multiply）
    napi_property_descriptor desc[] = {
        {
            "add",        // 属性名（在 ArkTS 中调用的名称）
            nullptr,      // 属性名（napi_value 形式，这里用字符串代替）
            Add,          // 原生函数指针
            nullptr,      // getter 函数
            nullptr,      // setter 函数
            nullptr,      // 属性值
            napi_default, // 属性特性
            nullptr       // 用户数据
        },

        {"multiply", nullptr, Multiply, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    // sizeof(desc) / sizeof(desc[0]) 计算数组元素个数
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

// 这是一个napi_module类型的静态变量，用于描述模块信息
static napi_module entryModule = {
    .nm_version = 1,            // N-API模块版本，这里为1
    .nm_flags = 0,              // 模块标志，0表示没有特殊标志
    .nm_filename = nullptr,     // 模块文件名，这里为nullptr，表示使用默认文件名
    .nm_register_func = Init,   // 模块注册函数，这里指定为Init函数
    .nm_modname = "entry",      // 模块名，这里为"entry"，在ArkTS中导入模块时使用
    .nm_priv = ((void*)0),      // 私有数据，这里为nullptr
    .reserved = { 0 },          // 保留字段，初始化为0
};

/**
 * 模块注册函数
 * 
 * 关键特性解析：
 *  extern "C"
 *      指定函数使用 C 语言的链接约定
 *      防止 C++ 编译器进行名称修饰
 *  __attribute__((constructor))
 *      GCC/Clang 编译器特性
 *      标记函数在程序加载时自动执行（在 main 函数之前）
 *      相当于静态构造函数
 *  napi_module_register(&demoModule)
 *      核心注册函数：向鸿蒙运行时注册原生模块
 *      参数：指向 napi_module 结构体的指针
 * 
 * 整个流程是：
 *      当动态库被加载时，RegisterEntryModule函数自动执行，注册demoModule模块。
 *      当ArkTS层导入这个模块时，会调用Init函数，将add和multiply函数绑定到 exports 对象上，这样ArkTS层就可以调用这两个原生函数了。
 */
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&entryModule);
}
